package gdma.hqdes.calc;

import common.util.RegexUtils;
import lombok.extern.slf4j.Slf4j;
import net.objecthunter.exp4j.Expression;
import net.objecthunter.exp4j.ExpressionBuilder;
import net.objecthunter.exp4j.function.Function;
import net.objecthunter.exp4j.operator.Operator;
import org.apache.commons.lang3.RegExUtils;

import java.util.Map;
import java.util.regex.Pattern;

/**
 * @Author wangyue
 * @Version 3.0
 * @Date 2024-07-14
 */
@Slf4j
public class FormulaCalculator implements ScoreCalculator {

    static Operator SAFE_DIV = new Operator("/", 2, true, Operator.PRECEDENCE_DIVISION) {
        @Override
        public double apply(double[] values) {
            double numerator = values[0];
            double denominator = values[1];
            if (denominator == 0) {
                if (numerator > 0) {
                    return Double.MAX_VALUE;
                } else if (numerator < 0) {
                    return Double.MIN_VALUE;
                } else {
                    return 0;
                }
            }
            return numerator / denominator;
        }
    };

    // 定义整除（求商）运算符 //
    Operator INT_DIV = new Operator("//", 2, true, Operator.PRECEDENCE_DIVISION + 1) {
        @Override
        public double apply(double... args) {
            if (args.length != 2) {
                throw new IllegalArgumentException("Division operator requires 2 arguments.");
            }
            return Math.floor(args[0] / args[1]);
        }
    };

    // 定义取余运算符 %%
    Operator MOD = new Operator("%%", 2, true, Operator.PRECEDENCE_MODULO + 1) {
        @Override
        public double apply(double... args) {
            if (args.length != 2) {
                throw new IllegalArgumentException("Modulo operator requires 2 arguments.");
            }
            return args[0] % args[1];
        }
    };

    static Operator PERCENT = new Operator("%", 1, true, Operator.PRECEDENCE_DIVISION) {
        @Override
        public double apply(double... args) {
            return args[0] / (double)100;
        }
    };

    static Operator CLAMP_MAX = new Operator("<", 2, true, Operator.PRECEDENCE_ADDITION - 1) {
        @Override
        public double apply(double... args) {
            if(args[0] > args[1]) return args[1];
            return args[0];
        }
    };

    static Function CLAMP = new Function("CLAMP", 3) {
        @Override
        public double apply(double... args) {
            if(args[0] < args[1]) return args[1];
            if(args[0] > args[2]) return args[2];
            return args[0];
        }
    };

    static Function IF_0 = new Function("IF_0", 3) {
        @Override
        public double apply(double... args) {
            return args[0] == 0 ? args[1] : args[2];
        }
    };

    static Function IF_GT_0 = new Function("IF_GT_0", 3) {
        @Override
        public double apply(double... args) {
            return args[0] > 0 ? args[1] : args[2];
        }
    };

    static Pattern VARIABLE_PATTERN = Pattern.compile("[a-z][a-z0-9]*");

    String formula;
    ExpressionBuilder expressionBuilder;

    public FormulaCalculator(String formula) {
        this.formula = doTidy(formula);

        expressionBuilder = new ExpressionBuilder(this.formula)
                .operator(SAFE_DIV, INT_DIV, MOD, PERCENT, CLAMP_MAX)
                .functions(CLAMP, IF_0, IF_GT_0);
        RegexUtils.findAll(this.formula, VARIABLE_PATTERN, matcher -> {
            expressionBuilder.variable(matcher.group());
        });
    }

    public static String doTidy(String rawString) {
        String tidyString = RegExUtils.removeAll(rawString, "\\s");
        tidyString = RegExUtils.replaceAll(tidyString, "×", "*");
        tidyString = RegExUtils.replaceAll(tidyString, "＊", "*");
        tidyString = RegExUtils.replaceAll(tidyString, "＋", "+");
        tidyString = RegExUtils.replaceAll(tidyString, "－", "-");
        tidyString = RegExUtils.replaceAll(tidyString, "（", "(");
        tidyString = RegExUtils.replaceAll(tidyString, "）", ")");
        tidyString = RegExUtils.replaceAll(tidyString, "\\[", "(");
        tidyString = RegExUtils.replaceAll(tidyString, "\\]", ")");

        return tidyString;
    }

    public float doCalc(Map<String, String> inputArgs) {
        // 构建表达式
        Expression expression = expressionBuilder.build();
        // 设置变量的值
        for(String name : expression.getVariableNames()) {
            String valueString = inputArgs.getOrDefault(name, "0");
            expression.setVariable(name, Double.parseDouble(valueString));
        }
        return (float)expression.evaluate();
    }

}
